//
//							FctorUtils.h
//

#ifndef __XLOKI_FCTOR_UTILS_H
#define __XLOKI_FCTOR_UTILS_H

#pragma message( "INCLUDING FILE: " __FILE__ )

#include <loki/Functor.h>
#include <loki/Typelist.h>
#include <loki/Sequence.h>

#include <loki/HierarchyGenerators.h>
#include <loki/static_check.h>

namespace xloki
{
	/************************************************************************/
	/*  Invoke                                                              */
	/************************************************************************/

	template < typename R, typename TypeList, int N >
	class Invoker;

	template < typename R, typename T, typename U, int N >
	class Invoker< R, Loki::Typelist< T, U >, N > 
	{
	public:
		template < typename TupleType >
		R operator() ( const Loki::Functor<R, Loki::Typelist<T, U> >& fctor, const TupleType& tuple )
		{
			const int TUPLE_LENGTH = Loki::TL::Length< typename TupleType::TList >::value;
			return Invoker< R, U, N - 1 > ()
				(
					Loki::BindFirst( fctor, Loki::Field< TUPLE_LENGTH - N > ( tuple ) ),
					tuple
				);
		}
	private:
		Loki::CompileTimeError< N == Loki::TL::Length < Loki::Typelist< T, U > >::value > ERROR_IncorrectParameterN;
	};

	template < typename R >
	class Invoker< R, Loki::NullType, 0 > 
	{
	public:
		template < typename TupleType >
		R operator() ( const Loki::Functor<R, Loki::NullType >& fctor, const TupleType& tuple )
		{
			return fctor();
		}
	};

	template < typename R, typename TypeList >
	R Invoke( const Loki::Functor< R, TypeList >& ftor, const Loki::Tuple< TypeList >& tuple )
	{
		return Invoker< R, TypeList, Loki::TL::Length< TypeList >::value > () ( ftor, tuple );
	}

	/************************************************************************/
	/*  MakeTuple                                                           */
	/************************************************************************/

	inline Loki::Tuple< Loki::Seq<>::Type > MakeTuple()
	{
		Loki::Tuple< Loki::Seq<>::Type > tuple;
		return tuple;
	}

	template < typename T1 >
	inline Loki::Tuple< typename Loki::Seq< T1 >::Type > MakeTuple( const T1& t1 )
	{
		Loki::Tuple< typename Loki::Seq< T1 >::Type > tuple;
		Loki::Field<0>( tuple ) = t1;
		return tuple;
	}

	template < typename T1, typename T2 >
	inline Loki::Tuple< typename Loki::Seq< T1, T2 >::Type > MakeTuple
		(
			const T1& t1,
			const T2& t2
		)
	{
		Loki::Tuple< typename Loki::Seq< T1, T2 >::Type > tuple;
		Loki::Field<0>( tuple ) = t1;
		Loki::Field<1>( tuple ) = t2;
		return tuple;
	}

	template < typename T1, typename T2, typename T3 >
	inline Loki::Tuple< typename LOKI_TYPELIST_3( T1, T2, T3 )/*Loki::Seq< T1, T2, T3 >::Type*/ > MakeTuple
		( 
			const T1& t1,
			const T2& t2,
			const T3& t3
		)
	{
		Loki::Tuple< typename Loki::Seq< T1, T2, T3 >::Type > tuple;
		Loki::Field<0>( tuple ) = t1;
		Loki::Field<1>( tuple ) = t2;
		Loki::Field<2>( tuple ) = t3;
		return tuple;
	}

	template < typename T1, typename T2, typename T3, typename T4 >
	inline Loki::Tuple< typename Loki::Seq< T1, T2, T3, T4 >::Type > MakeTuple
		( 
			const T1& t1,
			const T2& t2,
			const T3& t3,
			const T4& t4
		)
	{
		Loki::Tuple< typename Loki::Seq< T1, T2, T3, T4 >::Type > tuple;
		Loki::Field<0>( tuple ) = t1;
		Loki::Field<1>( tuple ) = t2;
		Loki::Field<2>( tuple ) = t3;
		Loki::Field<3>( tuple ) = t4;
		return tuple;
	}

	template < typename T1, typename T2, typename T3, typename T4, typename T5 >
	inline Loki::Tuple< typename Loki::Seq< T1, T2, T3, T4, T5 >::Type > MakeTuple
		( 
			const T1& t1,
			const T2& t2,
			const T3& t3,
			const T4& t4,
			const T5& t5
		)
	{
		Loki::Tuple< typename Loki::Seq< T1, T2, T3, T4, T5 >::Type > tuple;
		Loki::Field<0>( tuple ) = t1;
		Loki::Field<1>( tuple ) = t2;
		Loki::Field<2>( tuple ) = t3;
		Loki::Field<3>( tuple ) = t4;
		Loki::Field<4>( tuple ) = t5;
		return tuple;
	}
}

#endif
